package com.tpshion.cloud.auth.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.tpshion.cloud.auth.domain.dto.AddUserDto;
import com.tpshion.cloud.auth.domain.dto.EditUserDto;
import com.tpshion.cloud.auth.domain.dto.QueryUserDto;
import com.tpshion.cloud.auth.domain.dto.RegisterDto;
import com.tpshion.cloud.auth.domain.entity.*;
import com.tpshion.cloud.auth.domain.vo.LoginVo;
import com.tpshion.cloud.auth.domain.vo.MenuVo;
import com.tpshion.cloud.auth.mapper.*;
import com.tpshion.cloud.auth.utils.support.MenuTree;
import com.tpshion.cloud.auth.domain.vo.RegisterVo;
import com.tpshion.cloud.auth.domain.vo.UserDetailVo;
import com.tpshion.cloud.auth.service.UserService;
import com.tpshion.cloud.auth.utils.MenuUtil;
import com.tpshion.cloud.common.support.*;
import com.tpshion.cloud.common.utils.IdUtil;
import com.tpshion.cloud.common.utils.JsonUtil;
import com.tpshion.cloud.common.utils.JwtUtil;
import com.tpshion.cloud.core.autoconfig.support.MinioTemplate;
import com.tpshion.cloud.core.autoconfig.support.UploadResponse;
import com.tpshion.cloud.core.utils.ImgUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import java.io.IOException;
import java.io.InputStream;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Service
public class UserServiceImpl implements UserService {

    private static final Logger log = LoggerFactory.getLogger(UserServiceImpl.class);

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private PermissionMapper permissionMapper;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private MinioTemplate minioTemplate;

    @Autowired
    private MenuMapper menuMapper;

    @Autowired
    private RoleUserMapper roleUserMapper;

    @Autowired
    private RoleMapper roleMapper;

    private static final Map<String,Integer> userMap = new ConcurrentHashMap<>();

    /**
     * 使用账号登录
     * @param account 账号
     * @param password 密码
     * @return
     */
    @Override
    public Result loginByAccount(String account, String password) {
        log.info("userMap:{}",userMap);
        log.info("*** loginByAccount account:{}",account);
        User user = userMapper.findByAccount(account);
        //用户账号不存在
        Assert.notNull(user, ErrorCode.USER_ACCOUNT_NOT_EXIST);
        //检查用户状态是否正常
        checkUserState(user.getState());
        //校验密码
        if(!JwtUtil.checkpw(password,user.getPassword())){
            //记录密码错误次数
            Integer num = userMap.get(user.getId());
            if(null == num){
                userMap.put(user.getId(),0);
            }else{
                if(num >= 5){
                    //更新用户状态为锁定
                    user.setState(Constants.USER_L);
                    user.setUpdateTime(LocalDateTime.now());
                    log.info("user is lock. user:{}",user);
                    userMapper.updateById(user);
                    userMap.remove(user.getId());
                }else{
                    userMap.put(user.getId(),++num);
                }
            }
            throw new CoreException(ErrorCode.PASSWORAD_ERROR);
        }

        //清除密码错误次数
        if(null != userMap.get(user.getId())){
            userMap.remove(user.getId());
        }

        //生成token
        AuthInfo authInfo = new AuthInfo(user.getId());

        //统计登录次数
        loginTotal();

        return Result.ok(saveLoginInf(authInfo));
    }

    /**
     * 使用手机号登录
     * @param phone 手机号
     * @param password 密码
     * @return
     */
    @Override
    public Result loginByPhone(String phone, String password) {
        log.info("*** loginByPhone phone:{}",phone);
        List<User> users = userMapper.findByPhone(phone);
        Assert.notEmpty(users, ErrorCode.USER_PHONE_NOT_EXIST);
        Assert.isTrue(JwtUtil.checkpw(password,users.get(0).getPassword()),ErrorCode.PASSWORAD_ERROR);
        AuthInfo authInfo = new AuthInfo(users.get(0).getId());
        //统计登录次数
        loginTotal();
        return Result.ok(saveLoginInf(authInfo));
    }

    /**
     * 使用邮箱登录
     * @param email 邮箱
     * @param password 密码
     * @return
     */
    @Override
    public Result loginByEmail(String email, String password) {
        log.info("*** loginByEmail email:{}",password);
        List<User> users = userMapper.findByEmail(email);
        Assert.notEmpty(users, ErrorCode.USER_EMAIL_NOT_EXIST);
        Assert.isTrue(JwtUtil.checkpw(password,users.get(0).getPassword()),ErrorCode.PASSWORAD_ERROR);
        AuthInfo authInfo = new AuthInfo(users.get(0).getId());
        //统计登录次数
        loginTotal();
        return Result.ok(saveLoginInf(authInfo));
    }

    /**
     * 注册用户
     * @param registerDto
     * @return
     */
    @Override
    public Result register(RegisterDto registerDto) {
        // 校验账号是否存在
        Assert.isNull(userMapper.findByAccount(registerDto.getAccount()), ErrorCode.USER_ACCOUNT_IS_EXIST);
        // 如果手机号不为空，检查手机号是否已被使用
        if(StringUtils.isEmpty(registerDto.getPhone())){
            Assert.isEmpty(userMapper.findByPhone(registerDto.getPhone()), ErrorCode.USER_PHONE_IS_EXIST);
        }
        // 如果邮箱不为空，检查邮箱是否已被使用
        if(StringUtils.isEmpty(registerDto.getEmail())){
            Assert.isEmpty(userMapper.findByEmail(registerDto.getEmail()), ErrorCode.USER_EMAIL_IS_EXIST);
        }

        User user = new User();
        user.setId(IdUtil.getUUID());
        user.setUsername(StringUtils.isEmpty(registerDto.getUsername()) ? registerDto.getAccount() : registerDto.getUsername());
        user.setAccount(registerDto.getAccount());
        user.setPhone(registerDto.getPhone());
        user.setEmail(registerDto.getEmail());
        user.setPassword(JwtUtil.hashpw(registerDto.getPassword()));
        // 生成用户头像
        user.setUserImg(getUserImg(user.getUsername()));
        user.setState(Constants.USER_V);
        user.setCreateTime(LocalDateTime.now());

        if(userMapper.insert(user) > 0){
            RegisterVo registerVo = new RegisterVo();
            registerVo.setUserId(user.getId());
            BeanUtils.copyProperties(user,registerVo);
            return Result.ok(registerVo);
        }else{
            return Result.build(ErrorCode.REGISTER_ERROR);
        }
    }

    /**
     * 查询用户权限
     * @param userId
     * @return
     */
    @Override
    public List<Permission> findPermission(String userId) {

        return permissionMapper.findByUserId(userId);
    }

    /**
     * 根据用户id获取用户信息
     * @param userId 用户id
     * @return
     */
    @Override
    public User getUserInfo(String userId) {
        User user = userMapper.selectById(userId);
        Assert.notNull(user, ErrorCode.USER_NOT_EXIST);
        return user;
    }

    /**
     * 分页查询用户数据
     * @param queryUserDto 查询数据
     * @return
     */
    @Override
    public PageInfo getUserList(QueryUserDto queryUserDto) {
        PageHelper.startPage(queryUserDto.getPageNum(),queryUserDto.getPageSize());
        List<UserDetailVo> list = userMapper.getUserList(queryUserDto);
        return new PageInfo(list);
    }

    /**
     * 根据用户id获取用户菜单树
     * @param userId 用户id
     * @param sysCode 系统码
     * @return
     */
    @Override
    public MenuVo getMenuTree(String userId, String sysCode) {
        List<Menu> menus = menuMapper.findByUserId(userId, sysCode);
        log.info("*** menus:{}",menus);
        // 生成用户菜单树
        List<MenuTree> menuTrees = MenuUtil.getMenuTree(menus);
        List<String> access = new ArrayList<>();
        menus.stream().forEach(item ->{
            access.add(item.getName());
        });
        MenuVo menuVo = new MenuVo();
        menuVo.setAccess(access);
        menuVo.setMenuTrees(menuTrees);
        log.info("*** menuVo:{}",menuVo);
        return menuVo;
    }

    @Override
    @Transactional
    public UserDetailVo addUser(AddUserDto addUserDto) {
        // 校验账号是否存在
        Assert.isNull(userMapper.findByAccount(addUserDto.getAccount()), ErrorCode.USER_ACCOUNT_IS_EXIST);
        // 如果手机号不为空，检查手机号是否已被使用
        if(StringUtils.isEmpty(addUserDto.getPhone())){
            Assert.isEmpty(userMapper.findByPhone(addUserDto.getPhone()), ErrorCode.USER_PHONE_IS_EXIST);
        }
        // 如果邮箱不为空，检查邮箱是否已被使用
        if(StringUtils.isEmpty(addUserDto.getEmail())){
            Assert.isEmpty(userMapper.findByEmail(addUserDto.getEmail()), ErrorCode.USER_EMAIL_IS_EXIST);
        }

        User user = new User();
        user.setId(IdUtil.getUUID());
        user.setUsername(StringUtils.isEmpty(addUserDto.getUsername()) ? addUserDto.getAccount() : addUserDto.getUsername());
        user.setAccount(addUserDto.getAccount());
        user.setPhone(addUserDto.getPhone());
        user.setEmail(addUserDto.getEmail());
        user.setPassword(JwtUtil.hashpw(addUserDto.getPassword()));
        // 生成用户头像
        user.setUserImg(getUserImg(user.getUsername()));
        user.setState(addUserDto.getState());
        user.setNote(addUserDto.getNote());
        user.setCreateTime(LocalDateTime.now());
        userMapper.insert(user);

        UserDetailVo userDetailVo = new UserDetailVo();
        BeanUtils.copyProperties(user,userDetailVo);

        // 如果角色id不为空，则为用户分配角色
        if(!StringUtils.isEmpty(addUserDto.getRoleId())){
            // 查询角色是否存在
            Role role = roleMapper.selectById(addUserDto.getRoleId());
            Assert.notNull(role, ErrorCode.ROLE_NOT_EXIST);
            updateRole(addUserDto.getRoleId(),user.getId());
            userDetailVo.setRoleId(role.getId());
            userDetailVo.setRoleName(role.getRoleName());
            userDetailVo.setRoleNote(role.getNote());
        }

        return userDetailVo;
    }

    @Override
    @Transactional
    public UserDetailVo editUser(EditUserDto editUserDto) {
        // 查询用户是否存在
        User user = userMapper.selectById(editUserDto.getId());
        Assert.notNull(user, ErrorCode.USER_NOT_EXIST);

        // 如果手机号不为空，检查手机号是否已被使用
        if(!StringUtils.isEmpty(editUserDto.getPhone()) && !editUserDto.getPhone().equals(user.getPhone())){
            Assert.isEmpty(userMapper.findByPhone(editUserDto.getPhone()), ErrorCode.USER_PHONE_IS_EXIST);
        }
        // 如果邮箱不为空，检查邮箱是否已被使用
        if(!StringUtils.isEmpty(editUserDto.getEmail()) && !editUserDto.getEmail().equals(user.getEmail())){
            Assert.isEmpty(userMapper.findByEmail(editUserDto.getEmail()), ErrorCode.USER_EMAIL_IS_EXIST);
        }

        //修改用户信息
        user.setUsername(editUserDto.getUsername());
        user.setPhone(editUserDto.getPhone());
        user.setEmail(editUserDto.getEmail());
        user.setNote(editUserDto.getNote());
        user.setState(editUserDto.getState());
        user.setUpdateTime(LocalDateTime.now());
        userMapper.updateById(user);

        UserDetailVo userDetailVo = new UserDetailVo();
        BeanUtils.copyProperties(user,userDetailVo);

        // 如果角色id不为空，则为用户分配角色
        if(!StringUtils.isEmpty(editUserDto.getRoleId())){
            // 查询角色是否存在
            Role role = roleMapper.selectById(editUserDto.getRoleId());
            Assert.notNull(role, ErrorCode.ROLE_NOT_EXIST);
            updateRole(editUserDto.getRoleId(),user.getId());
            userDetailVo.setRoleId(role.getId());
            userDetailVo.setRoleName(role.getRoleName());
            userDetailVo.setRoleNote(role.getNote());
        }
        return userDetailVo;
    }

    /**
     * 保存登录用户信息
     * @param authInfo
     * @return
     */
    private LoginVo saveLoginInf(AuthInfo authInfo){
        //返回数据封装
        LoginVo loginVo = new LoginVo();
        loginVo.setUserId(authInfo.getUserId());

        String key = Field.TOKEN + "-" + authInfo.getUserId();
        // 判断是否登陆过，登录过直接返回有效的token
        String loginToken = getLoginToken(key);
        if(!StringUtils.isEmpty(loginToken)){
            loginVo.setToken(loginToken);
            log.info("*** login success loginVo:{}",loginVo);
            return loginVo;
        }

        String token = JwtUtil.createToken(authInfo);
        //使用redis保存用户token，已方便刷新token
        HashOperations<String,String,String> hashOperations = redisTemplate.opsForHash();
        //缓存token
        hashOperations.put(key, Field.TOKEN,token);
        //保存用户验证信息
        hashOperations.put(key, Field.AUTH_INF, JsonUtil.obj2String(authInfo));
        //设置token过期时间
        redisTemplate.expire(key, Constants.TOKEN_EXPIRE_TIME, TimeUnit.MINUTES);

        loginVo.setToken(token);
        log.info("*** login success loginVo:{}",loginVo);
        return loginVo;
    }

    private String getLoginToken(String key){
        HashOperations<String,String,String> hashOperations = redisTemplate.opsForHash();
        String redisToken = hashOperations.get(key, Field.TOKEN);
        if(StringUtils.isEmpty(redisToken)){
            return null;
        }
        Long expire = hashOperations.getOperations().getExpire(key, TimeUnit.MINUTES);
        if(expire < 0){
            return null;
        }
        return redisToken;
    }

    private String getUserImg(String username){
        InputStream inputStream = null;
        try {
            inputStream = ImgUtil.generateImg(username);
            UploadResponse response = minioTemplate.simpleUpload("userImg.png", inputStream, "image/png");
            return response.getFilePath();
        } catch (IOException e) {
            log.info("生成用户头像异常:{}",e.getMessage());
            throw new CoreException(ErrorCode.CREATE_USER_IMG_ERROR);
        }finally {
            if(inputStream != null){
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    private void checkUserState(String state){
        switch (state){
            case Constants.USER_A:
                throw new CoreException(ErrorCode.USER_IS_A);
            case Constants.USER_F:
                throw new CoreException(ErrorCode.USER_IS_F);
            case Constants.USER_L:
                throw new CoreException(ErrorCode.USER_IS_L);
            case Constants.USER_V:
                throw new CoreException(ErrorCode.USER_IS_V);
        }
    }

    /**
     * 更新角色信息
     */
    private void updateRole(String roleId, String userId){
        QueryWrapper<RoleUser> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("user_id", userId);
        List<RoleUser> roleUsers = roleUserMapper.selectList(queryWrapper);
        if(!roleUsers.isEmpty()){
            List<String> ids = roleUsers.stream().map(RoleUser::getId).collect(Collectors.toList());
            roleUserMapper.deleteBatchIds(ids);
        }
        RoleUser roleUser = new RoleUser();
        roleUser.setId(IdUtil.getUUID());
        roleUser.setUserId(userId);
        roleUser.setRoleId(roleId);
        roleUserMapper.insert(roleUser);
    }

    /**
     * 统计登录次数
     */
    private void loginTotal(){
        ValueOperations valueOperations = redisTemplate.opsForValue();
        Boolean absent = valueOperations.setIfAbsent(Field.LOGIN_NUM_KEY, 1);
        if(!absent){
            valueOperations.increment(Field.LOGIN_NUM_KEY);
        }
    }
}
